#include "IOUringTcpServer.h"
#include "Logger.h"
#include <functional>
#include <string.h>

using namespace std::placeholders;

void IOUringTcpServer::start() {
    listen();
    // start mainLoop
    InetAddress peerAddr;
    IOUringEventLoop mainLoop(listenFd_);
    mainLoop.setNewConnectionCallback(
        std::bind(newConnectionCallback_, &mainLoop, _1));
    mainLoop.setReadCompleteCallback(
        std::bind(readCompleteCallback_, &mainLoop, _1));
    mainLoop.setWriteCompleteCallback(
        std::bind(writeCompleteCallback_, &mainLoop, _1));
    mainLoop.addProvideBufAll();
    mainLoop.loop();
    // start subLoop
    if (numSubLoop_ > 0) {
        for (size_t i = 0; i < numSubLoop_; i++) {
            SubLoopThreadPtr subLoop =
                std::make_shared<IOUringEventLoopThread>(listenFd_);
            subLoop->setNewConnectionCallback(newConnectionCallback_);
            subLoop->setReadCompleteCallback(readCompleteCallback_);
            subLoop->setWriteCompleteCallback(writeCompleteCallback_);
            loops_.push_back(subLoop);
        }
        for (size_t i = 0; i < numSubLoop_; i++) {
            loops_[i]->startLoop();
        }
    }
}

void IOUringTcpServer::listen() {

    // setup socket
    listenFd_ = ::socket(AF_INET, SOCK_STREAM, 0);
    const int val = 1;
    ::setsockopt(listenFd_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

    memset(&servAddr_, 0, sizeof(servAddr_));
    servAddr_.sin_family = AF_INET;
    servAddr_.sin_port = htons(port_);
    servAddr_.sin_addr.s_addr = INADDR_ANY;

    // bind and listen
    if (::bind(listenFd_, (struct sockaddr *)&servAddr_, sizeof(servAddr_)) <
        0) {
        perror("Error binding socket...\n");
        exit(1);
    }
    if (::listen(listenFd_, 512) < 0) {
        perror("Error listening on socket...\n");
        exit(1);
    }
    LOG_DEBUG("io_uring echo server listening for connections on port: %d",
              port_);
}
